From c9b8a57cb77138884d02ba9319211aeee674c20d Mon Sep 17 00:00:00 2001 From: "cl349@freefall.cl.cam.ac.uk" Date: Fri, 6 Aug 2004 18:13:40 +0000 Subject: [PATCH] bitkeeper revision 1.1159.3.1 (4113ca544f2ijHD3gffCMUC9u9wOaQ) Make time code more robust. --- .../arch/xen/i386/kernel/time.c | 118 ++++++++++++++---- .../arch/xen/i386/kernel/timers/timer_tsc.c | 58 +-------- .../asm-xen/asm-i386/mach-xen/do_timer.h | 5 +- .../include/asm-xen/asm-i386/ptrace.h | 2 +- 4 files changed, 106 insertions(+), 77 deletions(-) diff --git a/linux-2.6.7-xen-sparse/arch/xen/i386/kernel/time.c b/linux-2.6.7-xen-sparse/arch/xen/i386/kernel/time.c index 82a8a91d81..c595ce1183 100644 --- a/linux-2.6.7-xen-sparse/arch/xen/i386/kernel/time.c +++ b/linux-2.6.7-xen-sparse/arch/xen/i386/kernel/time.c @@ -87,15 +87,71 @@ EXPORT_SYMBOL(i8253_lock); struct timer_opts *cur_timer = &timer_none; -extern u64 shadow_system_time; -extern u32 shadow_time_delta_usecs; -extern void __get_time_values_from_xen(void); +/* These are peridically updated in shared_info, and then copied here. */ +u32 shadow_tsc_stamp; +u64 shadow_system_time; +static u32 shadow_time_version; +static struct timeval shadow_tv; +extern u64 processed_system_time; + +#define NS_PER_TICK (1000000000ULL/HZ) + +/* + * Reads a consistent set of time-base values from Xen, into a shadow data + * area. Must be called with the xtime_lock held for writing. + */ +int __get_time_values_from_xen(void) +{ + s64 delta; + unsigned int ticks = 0; + + do { + shadow_time_version = HYPERVISOR_shared_info->time_version2; + rmb(); + shadow_tv.tv_sec = HYPERVISOR_shared_info->wc_sec; + shadow_tv.tv_usec = HYPERVISOR_shared_info->wc_usec; + shadow_tsc_stamp = HYPERVISOR_shared_info->tsc_timestamp.tsc_bits; + shadow_system_time = HYPERVISOR_shared_info->system_time; + rmb(); + } + while (shadow_time_version != HYPERVISOR_shared_info->time_version1); + + delta = (s64)(shadow_system_time + + cur_timer->get_offset() * NSEC_PER_USEC - + processed_system_time); + if (delta < 0) { + printk("Timer ISR: Time went backwards: %lld\n", delta); + return 1; + } + + if (delta < NS_PER_TICK) + return 1; + + /* Process elapsed jiffies since last call. */ + while (delta >= NS_PER_TICK) { + ticks++; + delta -= NS_PER_TICK; + processed_system_time += NS_PER_TICK; + } + jiffies_64 += ticks - 1; + /* We leave one tick for the caller to add to jiffies since + * the timer interrupt will call do_timer(). */ + + return 0; +} + +#define TIME_VALUES_UP_TO_DATE \ + (shadow_time_version == HYPERVISOR_shared_info->time_version2) + +/* + * We use this to ensure that gettimeofday() is monotonically increasing. We + * only break this guarantee if the wall clock jumps backwards "a long way". + */ +static struct timeval last_seen_tv = {0,0}; /* Keep track of last time we did processing/updating of jiffies and xtime. */ u64 processed_system_time; /* System time (ns) at last processing. */ -#define NS_PER_TICK (1000000000ULL/HZ) - /* * This version of gettimeofday has microsecond resolution * and better than microsecond precision on fast x86 machines with TSC. @@ -130,7 +186,22 @@ void do_gettimeofday(struct timeval *tv) usec += lost * (USEC_PER_SEC / HZ); sec = xtime.tv_sec; - usec += (xtime.tv_nsec / 1000); + usec += (xtime.tv_nsec / NSEC_PER_USEC); + + if (unlikely(!TIME_VALUES_UP_TO_DATE)) { + /* + * We may have blocked for a long time, + * rendering our calculations invalid + * (e.g. the time delta may have + * overflowed). Detect that and recalculate + * with fresh values. + */ + write_seqlock(&xtime_lock); + if (__get_time_values_from_xen() == 0) + jiffies_64++; + write_sequnlock(&xtime_lock); + continue; + } } while (read_seqretry(&xtime_lock, seq)); while (usec >= 1000000) { @@ -138,6 +209,16 @@ void do_gettimeofday(struct timeval *tv) sec++; } + /* Ensure that time-of-day is monotonically increasing. */ + if ((sec < last_seen_tv.tv_sec) || + ((sec == last_seen_tv.tv_sec) && (usec < last_seen_tv.tv_usec))) { + sec = last_seen_tv.tv_sec; + usec = last_seen_tv.tv_usec; + } else { + last_seen_tv.tv_sec = sec; + last_seen_tv.tv_usec = usec; + } + tv->tv_sec = sec; tv->tv_usec = usec; } @@ -172,6 +253,9 @@ int do_settimeofday(struct timespec *tv) time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; + + last_seen_tv.tv_sec = 0; + write_sequnlock_irq(&xtime_lock); clock_was_set(); return 0; @@ -236,8 +320,7 @@ static inline void do_timer_interrupt(int irq, void *dev_id, } #endif - if (regs) - do_timer_interrupt_hook(regs); + do_timer_interrupt_hook(regs); #if 0 /* XEN PRIV */ /* @@ -288,8 +371,6 @@ static inline void do_timer_interrupt(int irq, void *dev_id, */ irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - s64 delta; - /* * Here we are in the timer irq handler. We just have irqs locally * disabled but we don't know if the timer_bh is running on the other @@ -299,17 +380,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) */ write_seqlock(&xtime_lock); - __get_time_values_from_xen(); - - shadow_time_delta_usecs = cur_timer->get_offset() * NSEC_PER_USEC; - delta = (s64)(shadow_system_time + shadow_time_delta_usecs - - processed_system_time); - if (delta < 0) { - printk("Timer ISR: Time went backwards: %lld\n", delta); - goto out; - } - - if (delta < NS_PER_TICK) + if (__get_time_values_from_xen()) goto out; cur_timer->mark_offset(); @@ -426,12 +497,15 @@ void __init time_init(void) #endif xtime.tv_sec = HYPERVISOR_shared_info->wc_sec; wall_to_monotonic.tv_sec = -xtime.tv_sec; - xtime.tv_nsec = HYPERVISOR_shared_info->wc_usec * 1000; + xtime.tv_nsec = HYPERVISOR_shared_info->wc_usec * NSEC_PER_USEC; wall_to_monotonic.tv_nsec = -xtime.tv_nsec; cur_timer = select_timer(); printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name); + __get_time_values_from_xen(); + processed_system_time = shadow_system_time; + time_irq = bind_virq_to_irq(VIRQ_TIMER); (void)setup_irq(time_irq, &irq_timer); diff --git a/linux-2.6.7-xen-sparse/arch/xen/i386/kernel/timers/timer_tsc.c b/linux-2.6.7-xen-sparse/arch/xen/i386/kernel/timers/timer_tsc.c index 623b85c9de..87bc7f32b5 100644 --- a/linux-2.6.7-xen-sparse/arch/xen/i386/kernel/timers/timer_tsc.c +++ b/linux-2.6.7-xen-sparse/arch/xen/i386/kernel/timers/timer_tsc.c @@ -79,39 +79,9 @@ static int count2; /* counter for mark_offset_tsc() */ */ static unsigned long fast_gettimeoffset_quotient; - -/* These are peridically updated in shared_info, and then copied here. */ -static u32 shadow_tsc_stamp; -u64 shadow_system_time; -static u32 shadow_time_version; -static struct timeval shadow_tv; -u32 shadow_time_delta_usecs; -static unsigned int rdtsc_bitshift; -extern u64 processed_system_time; - -#define NS_PER_TICK (1000000000ULL/HZ) - -/* - * Reads a consistent set of time-base values from Xen, into a shadow data - * area. Must be called with the xtime_lock held for writing. - */ -void __get_time_values_from_xen(void) -{ - do { - shadow_time_version = HYPERVISOR_shared_info->time_version2; - rmb(); - shadow_tv.tv_sec = HYPERVISOR_shared_info->wc_sec; - shadow_tv.tv_usec = HYPERVISOR_shared_info->wc_usec; - shadow_tsc_stamp = HYPERVISOR_shared_info->tsc_timestamp.tsc_bits; - shadow_system_time = HYPERVISOR_shared_info->system_time; - rmb(); - } - while (shadow_time_version != HYPERVISOR_shared_info->time_version1); -} - -#define TIME_VALUES_UP_TO_DATE \ - (shadow_time_version == HYPERVISOR_shared_info->time_version2) - +unsigned int rdtsc_bitshift; +extern u32 shadow_tsc_stamp; +extern u64 shadow_system_time; static unsigned long get_offset_tsc(void) { @@ -188,23 +158,9 @@ unsigned long long sched_clock(void) static void mark_offset_tsc(void) { - s64 delta; - unsigned int ticks = 0; - - write_seqlock(&monotonic_lock); - - delta = (s64)(shadow_system_time + shadow_time_delta_usecs - - processed_system_time); - - /* Process elapsed jiffies since last call. */ - while (delta >= NS_PER_TICK) { - ticks++; - delta -= NS_PER_TICK; - processed_system_time += NS_PER_TICK; - } - jiffies_64 += ticks - 1; /* update the monotonic base value */ + write_seqlock(&monotonic_lock); monotonic_base = shadow_system_time; monotonic_offset = shadow_tsc_stamp; write_sequnlock(&monotonic_lock); @@ -383,7 +339,8 @@ static int __init init_tsc(char* override) printk(KERN_INFO "Xen reported: %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); - /* (10^6 * 2^32) / cpu_khz = (2^32 * 1 / (clocks/us)) */ + /* (10^6 * 2^32) / cpu_hz = (10^3 * 2^32) / cpu_khz = + (2^32 * 1 / (clocks/us)) */ { unsigned long eax=0, edx=1000; __asm__("divl %2" @@ -396,9 +353,6 @@ static int __init init_tsc(char* override) set_cyc2ns_scale(cpu_khz/1000); - __get_time_values_from_xen(); - processed_system_time = shadow_system_time; - rdtscll(alarm); return 0; diff --git a/linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/mach-xen/do_timer.h b/linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/mach-xen/do_timer.h index 8ac79726f5..565ba968cd 100644 --- a/linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/mach-xen/do_timer.h +++ b/linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/mach-xen/do_timer.h @@ -22,9 +22,10 @@ static inline void do_timer_interrupt_hook(struct pt_regs *regs) * system, in that case we have to call the local interrupt handler. */ #ifndef CONFIG_X86_LOCAL_APIC - x86_do_profile(regs); + if (regs) + x86_do_profile(regs); #else - if (!using_apic_timer) + if (regs && !using_apic_timer) smp_local_timer_interrupt(regs); #endif } diff --git a/linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/ptrace.h b/linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/ptrace.h index 0165ccc542..eabe6d3267 100644 --- a/linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/ptrace.h +++ b/linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/ptrace.h @@ -55,7 +55,7 @@ struct pt_regs { #define PTRACE_SET_THREAD_AREA 26 #ifdef __KERNEL__ -#define user_mode(regs) ((VM_MASK & (regs)->eflags) || (2 & (regs)->xcs)) +#define user_mode(regs) ((regs) && ((VM_MASK & (regs)->eflags) || (2 & (regs)->xcs))) #define instruction_pointer(regs) ((regs)->eip) #endif -- 2.30.2